import 'dart:math' as math show sin, pi;

import 'package:flutter/widgets.dart';

/// using package[flutter_spinkit](https://pub.dev/packages/flutter_spinkit)
/// 在原有类基础上添加功能：自定义圆点个数[pointCount]
/// color & gradient必须有一个不为空，gradient不为空，优先使用gradient
class FadingCircleLoading extends StatefulWidget {
  const FadingCircleLoading({
    super.key,
    this.color,
    this.gradient,
    this.size = 40.0,
    this.pointCount = 8,
    this.itemBuilder,
    this.duration = const Duration(milliseconds: 1200),
    this.controller,
  }) : assert(
            !(itemBuilder is IndexedWidgetBuilder && color is Color) &&
                !(itemBuilder == null && color == null && gradient == null),
            'You should specify either a itemBuilder or a color');

  final Color? color;
  final Gradient? gradient;
  final double size;
  final int pointCount;
  final IndexedWidgetBuilder? itemBuilder;
  final Duration duration;
  final AnimationController? controller;

  @override
  FadingCircleLoadingState createState() => FadingCircleLoadingState();
}

class FadingCircleLoadingState extends State<FadingCircleLoading>
    with SingleTickerProviderStateMixin {
  final List<double> delays = [
    .0,
    -1.1,
    -1.0,
    -0.9,
    -0.8,
    -0.7,
    -0.6,
    -0.5,
    -0.4,
    -0.3,
    -0.2,
    -0.1
  ];
  late AnimationController _controller;

  @override
  void initState() {
    super.initState();
    _controller = (widget.controller ??
        AnimationController(vsync: this, duration: widget.duration))
      ..repeat();
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Center(
      child: SizedBox.fromSize(
        size: Size.square(widget.size),
        child: Stack(
          children: List.generate(widget.pointCount, (i) {
            final position = widget.size * .5;
            return Positioned.fill(
              left: position,
              top: position,
              child: Transform(
                transform:
                    Matrix4.rotationZ(360 / widget.pointCount * i * 0.0174533),
                child: Align(
                  alignment: Alignment.center,
                  child: ScaleTransition(
                    scale: DelayTween(begin: 0.0, end: 1.0, delay: delays[i])
                        .animate(_controller),
                    child: SizedBox.fromSize(
                        size: Size.square(widget.size * 0.15),
                        child: _itemBuilder(i)),
                  ),
                ),
              ),
            );
          }),
        ),
      ),
    );
  }

  Widget _itemBuilder(int index) =>
      widget.itemBuilder?.call(context, index) ?? _buildDecoratedBox();

  DecoratedBox _buildDecoratedBox() {
    if (widget.gradient != null) {
      return DecoratedBox(
          decoration: BoxDecoration(
        gradient: widget.gradient,
        shape: BoxShape.circle,
      ));
    } else {
      return DecoratedBox(
          decoration: BoxDecoration(
        color: widget.color,
        shape: BoxShape.circle,
      ));
    }
  }
}

class DelayTween extends Tween<double> {
  DelayTween({
    required double begin,
    required double end,
    required this.delay,
  }) : super(begin: begin, end: end);

  final double delay;

  @override
  double lerp(double t) =>
      super.lerp((math.sin((t - delay) * 2 * math.pi) + 1) / 2);

  @override
  double evaluate(Animation<double> animation) => lerp(animation.value);
}
